Scenario #9690: A Global Admin Can Create a Read Only Api Key Subject

The endpoint-scope *:read makes an API-key read-only: it allows all GET endpoints under /api/, but nothing which changes data. With the global ADMIN role granted to its API_KEY subject, such an API-key can read everything, e.g. for reporting or monitoring, without any risk of modifying data.

Properties

Given

name value
subjectUuid a91c0009-0000-0000-0000-000000000009
subjectName readonly.key

Create the read-only API_KEY Subject

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "type" : "API_KEY",
  "scopes" : [ "*:read" ]
}
EOF
=> status: 201 CREATED a91c0009-0000-0000-0000-000000000009
{
  "uuid" : "a91c0009-0000-0000-0000-000000000009",
  "name" : "readonly.key",
  "organization" : "readonly",
  "type" : "API_KEY",
  "apiKey" : "hsak_readonly.key.7b7bd0324479df9341a5ad3acf47ad15288623d97272408740639b863655a931",
  "scopes" : [ "*:read" ],
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "589a6260-757d-4e3e-a2b7-08ca8b758868", // globalAdminRoleUuidToGrant
  "object.uuid" : "9b7b3d20-a88f-4c19-8bc7-efdb8dd24832",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#9b7b3d20-a88f-4c19-8bc7-efdb8dd24832:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "589a6260-757d-4e3e-a2b7-08ca8b758868", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0009-0000-0000-0000-000000000009"
}
EOF
=> status: 201 CREATED 589a6260-757d-4e3e-a2b7-08ca8b758868 // globalAdminRoleUuidToGrant

Verify the read-only API-key can use GET endpoints, e.g. list all memberships

HTTP GET "/api/hs/office/memberships" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[
  {
    "uuid" : "ef1b1ad5-3f35-4171-8714-fdaaed78109b", // Membership: M-3101000 - Test AG
    "partner" : {
      "uuid" : "4db6b8bf-d1f9-4a92-953c-787e345acb22", // Partner: P-31010 - Test AG
      "partnerNumber" : "P-31010",
      "partnerRel" : {
        "uuid" : "da7c6690-455b-404c-aac3-fa98660e5a7d", // partnerRelationUuid
        "anchor" : {
          "uuid" : "8f6b2e83-5cd0-4f3b-bac9-c56b374445cf",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "3029ef4b-ab91-429d-855b-26a8bedcef6e", // Person: Test AG
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Test AG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "ab067924-4231-4dce-a465-f768d7d54c2a", // Contact: Test AG - China
          "caption" : "Test AG - China",
          "postalAddress" : {
            "country" : "China",
            "province" : "Guangdong Province",
            "city" : "Dongguan City",
            "street" : "No.2 Commercial Second Street",
            "district" : "Niushan Wei Wu",
            "department" : "Executive Board",
            "building" : "Thi Chi Koh Building"
          },
          "emailAddresses" : {
            "main" : "norden@test-ag.example.org"
          },
          "phoneNumbers" : {
            "phone" : "++15 999 654321"
          }
        }
      },
      "details" : {
        "uuid" : "bbb58ff5-d4bf-4d7f-b6a4-e40af4f0142f",
        "registrationOffice" : "Registergericht Hamburg",
        "registrationNumber" : "1234567",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-3101000",
    "memberNumberSuffix" : "00",
    "validFrom" : "2020-10-15",
    "validTo" : "2023-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "1d63e978-d234-4d48-82c5-a17052fe7144",
    "partner" : {
      "uuid" : "62cee98c-9f21-4209-a2dc-82b2a8df5ed8",
      "partnerNumber" : "P-10001",
      "partnerRel" : {
        "uuid" : "b4787a32-0b35-4cb1-818c-b63c28ef8378",
        "anchor" : {
          "uuid" : "8f6b2e83-5cd0-4f3b-bac9-c56b374445cf",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "63b26b7f-9bc0-4f20-a954-5b6f52f8df23", // Person: First GmbH
          "personType" : "LEGAL_PERSON",
          "tradeName" : "First GmbH",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "32e7083d-6ab9-4a0d-90ea-ca695082d0c7", // Contact: contact-admin@firstcontact.example.com
          "caption" : "first contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@firstcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "5bf6434e-d41f-4270-bea4-18defffef39c",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000101",
    "memberNumberSuffix" : "01",
    "validFrom" : "2022-10-01",
    "validTo" : "2024-12-30",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  {
    "uuid" : "28f8ec18-758c-438a-b83a-1c5172a9b14a",
    "partner" : {
      "uuid" : "296cbb96-f172-4143-98cd-30d9453e898b", // partnerUuid
      "partnerNumber" : "P-10002",
      "partnerRel" : {
        "uuid" : "cef8ec7e-f336-4eb6-b8af-64dc374f7a46",
        "anchor" : {
          "uuid" : "8f6b2e83-5cd0-4f3b-bac9-c56b374445cf",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Hostsharing eG",
          "salutation" : null,
          "title" : null,
          "givenName" : null,
          "familyName" : null
        },
        "holder" : {
          "uuid" : "81d77669-fb74-4434-b15e-481e17ada98d",
          "personType" : "LEGAL_PERSON",
          "tradeName" : "Peter Smith - The Second Hand and Thrift Stores-n-Shipping e.K.",
          "salutation" : null,
          "title" : null,
          "givenName" : "Peter",
          "familyName" : "Smith"
        },
        "type" : "PARTNER",
        "mark" : null,
        "contact" : {
          "uuid" : "ab23ab66-5b24-44c3-8155-d391b3ff851d",
          "caption" : "second contact",
          "postalAddress" : {
            "country" : "Germany"
          },
          "emailAddresses" : {
            "main" : "contact-admin@secondcontact.example.com"
          },
          "phoneNumbers" : {
            "phone_office" : "+49 123 1234567"
          }
        }
      },
      "details" : {
        "uuid" : "1e874d3a-9310-46c1-876d-134e80da0cdc",
        "registrationOffice" : "Hamburg",
        "registrationNumber" : "RegNo123456789",
        "birthName" : null,
        "birthPlace" : null,
        "birthday" : null,
        "dateOfDeath" : null
      }
    },
    "mainDebitor" : null,
    "memberNumber" : "M-1000202",
    "memberNumberSuffix" : "02",
    "validFrom" : "2022-10-01",
    "validTo" : "2025-12-31",
    "status" : "CANCELLED",
    "membershipFeeBillable" : true
  },
  "..."
]

Verify the read-only API-key cannot write, e.g. not upsert a subject

HTTP PUT "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY" \
  <<EOF
{
  "name" : "hsh-never_updated",
  "type" : "USER"
}
EOF
=> status: 403 FORBIDDEN 
{
  "path" : "/api/rbac/subjects/a91c0009-0000-0000-0000-000000000009",
  "statusCode" : 403,
  "statusPhrase" : "Forbidden",
  "message" : "ERROR: [403] API-key scopes do not allow PUT /api/rbac/subjects/a91c0009-0000-0000-0000-000000000009"
}

generated on 2026-08-10 03:08:48 for branch HEAD